/*
 * MultipartStream.cpp
 *
 *  Created on: 2017年11月8日
 *      Author: zhengboyuan
 */

#include "MultipartStream.h"
#include "Path.h"
#include <fstream>
#include <strstream>
#include <sstream>
#include "TStringUtil.h"



static const char* Content_Disposition = "Content-Disposition: form-data;";
static const char* Blank = " ";
static const char* Content_Type = "Content-Type: ";
static const char* EndOfLine = "\r\n";



class TextDataStream : public DataStream
{
public:
	explicit TextDataStream(const std::string& text):
		m_text(text),
		m_stream(text)
	{
	}

	virtual size_t getLength()
	{
		return m_text.length();
	}

	virtual std::istream& getStream()
	{
		return m_stream;
	}

	const std::string& getText() const
	{
		return m_text;
	}

protected:
	std::string	m_text;
	std::istringstream	m_stream;
};

class FileDataStream : public DataStream
{
public:
	explicit FileDataStream(const std::string& filepath) :
		m_filepath(filepath),
		m_stream()
	{
		m_fileSize = (size_t)comn::Path::getSize(m_filepath);
		m_stream.open(filepath.c_str(), std::ios::binary);
	}

	virtual size_t getLength()
	{
		return m_fileSize;
	}

	virtual std::istream& getStream()
	{
		return m_stream;
	}

	const std::string& getFilePath() const
	{
		return m_filepath;
	}

protected:
	std::string		m_filepath;
	std::ifstream	m_stream;
	size_t			m_fileSize;
};


static void print(DataStream* stream)
{
	TextDataStream* textStream = dynamic_cast<TextDataStream*>(stream);
	FileDataStream* fileStream = dynamic_cast<FileDataStream*>(stream);

	if (textStream != NULL)
	{
		printf("text %d. %s\n", textStream->getLength(), textStream->getText().c_str());
	}
	else if (fileStream != NULL)
	{
		printf("file %d. %s\n", fileStream->getLength(), fileStream->getFilePath().c_str());
	}
	else
	{
		printf("data %d.\n", stream->getLength());
	}
}





MultipartStream::MultipartStream()
{
	m_boundary = makeBoundary();
}

MultipartStream::~MultipartStream()
{
}

/// 边界.
const std::string& MultipartStream::boundary() const
{
	return m_boundary;
}

/**
* 添加表数据分段, 可以多次调用.
* @param name	名称
* @param data	数据
* @return true 表示成功
*/
bool MultipartStream::addFormData(const std::string& name, const std::string& data)
{
	if (name.empty())
	{
		return false;
	}

	PartStream partStream;
	partStream.header["name"] = name;
	partStream.stream.reset(new TextDataStream(data));
	m_parts.push_back(partStream);

	return true;
}

bool MultipartStream::addFormData(const StringMap& formData)
{
	StringMap::const_iterator it = formData.begin();
	for (; it != formData.end(); ++it)
	{
		addFormData(it->first, it->second);
	}
	return true;
}

bool MultipartStream::addFile(const std::string& name, const std::string& type, const std::string& filepath)
{
	PartStream partStream;

	std::string filename = comn::Path::getFileName(filepath);

	partStream.header["name"] = name;
	partStream.header["filename"] = filename;
	
	partStream.type = type;

	partStream.stream.reset(new FileDataStream(filepath));

	m_parts.push_back(partStream);

	return true;
}

std::string MultipartStream::makeBoundary()
{
	return "--------------------------011144126994496129403969";
	//return comn::StringUtil::format("---------------------------%s", this);
}


int MultipartStream::read(char* buffer, int size)
{
	std::streamsize length = readData(buffer, size);
	if (length == 0)
	{
		length = readData(buffer, size);
	}
	
	return (int)length;
}

int MultipartStream::readData(char* buffer, int size)
{
	if (m_dataStreams.empty())
	{
		return 0;
	}

	DataStreamPtr dataStream = m_dataStreams.front();
	std::istream& istream = dataStream->getStream();
	istream.read(buffer, size);
	std::streamsize length = istream.gcount();
	if (length < size)
	{
		//print(dataStream.get());
		m_dataStreams.pop_front();
	}

	return (int)length;
}

int MultipartStream::getLength()
{
	if (m_dataStreams.empty())
	{
		makeDataStream();
	}

	int length = 0;

	for (size_t i = 0; i < m_dataStreams.size(); ++i)
	{
		DataStreamPtr& stream = m_dataStreams[i];
		length += stream->getLength();
	}

	return length;
}

int MultipartStream::makeDataStream(PartStream& partStream)
{
	std::ostringstream ss;
	ss << Content_Disposition;

	size_t count = 0;
	StringMap::const_iterator itName = partStream.header.find("name");
	if (itName != partStream.header.end())
	{
		ss << " ";
		ss << itName->first;
		ss << "=\"";
		ss << itName->second;
		ss << "\"";
		count++;
	}

	StringMap::const_iterator it = partStream.header.begin();
	for (; it != partStream.header.end(); ++it)
	{
		if (it == itName)
		{
			continue;
		}

		if (count == 0)
		{
			ss << " ";
		}
		else
		{
			ss << "; ";
		}

		ss << it->first;
		ss << "=\"";
		ss << it->second;
		ss << "\"";
	}
	ss << "\r\n";

	if (partStream.type.size() > 0)
	{
		ss << "Content-Type: ";
		ss << partStream.type;
		ss << "\r\n";
	}

	ss << "\r\n";

	{
		std::string text = ss.str();
		DataStreamPtr stream(new TextDataStream(text));
		m_dataStreams.push_back(stream);
	}

	m_dataStreams.push_back(partStream.stream);

	return 3;
}

int MultipartStream::makeDataStream()
{
	int size = 0;

	for (size_t i = 0; i < m_parts.size(); i++)
	{
		if (i == 0)
		{
			size += makeFirstBoundary();
		}
		else
		{
			size += makeMiddleBoundary();
		}

		size += makeDataStream(m_parts[i]);
	}

	size += makeLastBoundary();
	return size;
}

int MultipartStream::makeFirstBoundary()
{
	std::ostringstream ss;
	ss << "--";
	ss << m_boundary;
	ss << EndOfLine;
	std::string text = ss.str();
	DataStreamPtr stream(new TextDataStream(text));
	m_dataStreams.push_back(stream);
	return 1;
}

int MultipartStream::makeLastBoundary()
{
	std::ostringstream ss;
	ss << EndOfLine;
	ss << "--";
	ss << m_boundary;
	ss << "--";
	ss << EndOfLine;
	std::string text = ss.str();
	DataStreamPtr stream(new TextDataStream(text));
	m_dataStreams.push_back(stream);
	return 1;
}

int MultipartStream::makeMiddleBoundary()
{
	std::ostringstream ss;
	ss << EndOfLine;
	ss << "--";
	ss << m_boundary;
	ss << EndOfLine;
	std::string text = ss.str();
	DataStreamPtr stream(new TextDataStream(text));
	m_dataStreams.push_back(stream);
	return 1;
}